#pragma once
#include <iostream>
#include <queue>
const int gMaxCap=5;
template <class T>
class BlockQueue
{
public:
    BlockQueue(const int capacity=gMaxCap)
    :_capacity(capacity)
    {
        pthread_mutex_init(&_mutex,nullptr);
        pthread_cond_init(&_pcond,nullptr);
        pthread_cond_init(&_ccond,nullptr);
    }
    void push(const T& in)//输入型参数：const &
    {
        pthread_mutex_lock(&_mutex);
        //细节2：充当条件的判断必须是while，不能用if
        //这是因为唤醒的时候存在唤醒异常或伪唤醒的情况
        //需要让线程重新使用IsFull对空间就行判断，确保100%唤醒
        while(IsFull())
        {
            //细节1：
            //该线程被pthread_cond_wait函数挂起后，会自动释放锁。
            //该线程被pthread_cond_signal函数唤醒后，会自动重新获取原来那把锁
            pthread_cond_wait(&_pcond,&_mutex);//因为生产条件不满足，无法生产，此时我们的生产者进行等待
        }
        //走到这里一定没有满
        _q.push(in);
        //刚push了一个数据，可以试着消费者把他取出来(唤醒消费者)
        //细节3：pthread_cond_signal()这个函数，可以放在临界区内部，也可以放在临界区外部
        pthread_cond_signal(&_ccond);//可以设置水位线，满多少就唤醒消费者
        pthread_mutex_unlock(&_mutex);
        //pthread_cond_signal(&_ccond);//也可以放在解锁之后
    }
    void pop(T* out)//输出型参数：*  //输入输出型：&
    {
        pthread_mutex_lock(&_mutex);
        while(IsEmpty())
        {
            pthread_cond_wait(&_ccond,&_mutex);//消费者休眠
        }
        //先把数据处理好，再唤醒消费者
        *out=_q.front();
        _q.pop();
        //走到这里，一定能保证不为空，唤醒生产者进行生产
        pthread_cond_signal(&_pcond);
        pthread_mutex_unlock(&_mutex);
    }
    ~BlockQueue()
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_pcond);
        pthread_cond_destroy(&_ccond);
    }
private:
    bool IsEmpty()
    {
        return _q.empty();
    }
    bool IsFull()
    {
        return _q.size()==_capacity;
    }
private:
    std::queue<T> _q;
    int _capacity;//阻塞队列的容量
    pthread_mutex_t _mutex;//互斥锁
    pthread_cond_t _pcond;//生产者对应的条件变量
    pthread_cond_t _ccond;//消费者对应的条件变量
};